home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Language/OS - Multiplatform Resource Library
/
LANGUAGE OS.iso
/
gnu
/
smaltalk.lha
/
smalltalk-1.1.1
/
mstoop.c
< prev
next >
Wrap
C/C++ Source or Header
|
1991-09-12
|
31KB
|
1,275 lines
/***********************************************************************
*
* Object Table maintenance module.
*
***********************************************************************/
/***********************************************************************
*
* Copyright (C) 1990, 1991 Free Software Foundation, Inc.
* Written by Steve Byrne.
*
* This file is part of GNU Smalltalk.
*
* GNU Smalltalk is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 1, or (at your option) any later
* version.
*
* GNU Smalltalk is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* GNU Smalltalk; see the file COPYING. If not, write to the Free Software
* Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*
***********************************************************************/
/*
* Change Log
* ============================================================================
* Author Date Change
* sbyrne 8 Apr 90 Changed oopFree to oopValid to fix the bug with
* someInstance losing after GC's due to objects that
* have non-free OOP table entries, but point to freed
* objects.
*
* sbyrne 7 Apr 90 Increased mem space size to 4M. This can be
* decreased as necessary.
*
* sbyrne 24 Feb 90 Update to change log: there are no longer any
* explicitly allocated OOPs due to the new symbol table
* structure; the comment below is now a noop.
*
* sbyrne 20 Sep 89 Added oop table slot GC'ing. I'm not dealing with
* oop table slots that are explictly allocated; I
* believe that most OOP slots are not explicitly chosen
* and so not running the incremental reclaimer for that
* case shouldn't hurt us.
*
* sbyrne 12 Sep 89 Much of the garbage collector's operation depends on
* the fact that only 1 flip will occur between any two
* operations (such as a compilation, or a byte-code).
* The code would be much more complex if this were not
* the case, and I'm not sure that things would even be
* possible if this were not the case. Anyway, there is
* code in this routine to check for that eventuality
* and to halt the system if it occurs.
*
* sbyrne 6 Sep 89 started implementing the garbage collector (YAY!!!)
*
* sbyrne 13 Jan 89 Created.
*
*/
#include <stdio.h>
#include "mst.h"
#include "mstoop.h"
#include "mstdict.h"
#include "mstsave.h"
#include "mstcomp.h"
/* Size of the object semi spaces, in bytes */
#define K 1024
#ifndef atarist
#define MEM_SPACE_SIZE (4 * K * K)
#else
#define MEM_SPACE_SIZE (1152 * K)
#endif
#define oopReclaimFactor 4 /* how many oops to reclaim per oop alloc */
/* #define INLINE_MACROS */
/* Define this flag to turn on debugging code for OOP table management */
/* #define OOP_DEBUGGING */
#define alignSize(size) \
( ((size) + DOUBLE_ALIGNMENT - 1) & ~(DOUBLE_ALIGNMENT - 1) )
#define objSpace(obj) \
((char *)(obj) >= spaces[1].space)
typedef struct CompiledMethodStruct *Method;
extern Boolean regressionTesting;
/* These are the real OOPS for nil, true, and false */
OOP nilOOP, trueOOP, falseOOP;
/* The OOP table. This contains a pointer to the object, and some flag
bits indicating which semispace the pointed-to object lives in. Some
of the bits indicate the difference between the allocated length (stored
in the object itself, and the real length, for things like byte strings
that may not be an even multiple of 4 (== sizeof(void *)). */
struct OOPStruct oopTable[TOTAL_OOP_TABLE_SLOTS];
/* This is the head of the free list. The free list is maintained in the
oop table. Each OOP on the free list has a bit indicating that it's free,
a pointer to the next free OOP, and a pointer to the previous free OOP.
when this points at NIL, we're out of space */
OOP freeOOPs;
int numFreeOOPs;
/* The indices of which is the current new space (toSpace) and which is
the current old space (fromSpace). At GC flip time, these two are
interchanged. */
char/*int*/ fromSpace, toSpace;
Boolean gcFlipped, gcState, gcMessage;
int gcFlipCounter;
/* This vector holds the storage for all the Character objects in the system.
Since all character objects are unique, we pre-allocate space for 256 of
them, and treat them as special built-ins when doing garbage collection.*/
CharObject charObjectTable[NUM_CHAR_OBJECTS];
/* This is "nil" object in the system. That is, the single instance of the
UndefinedObject class, which is called "nil". */
struct NilObjectStruct nilObject;
/* These represent the two boolean objects in the system, true and false.
This is the object storage for those two objects.
false == &booleanObjects[0], true == &booleanObjects[1] */
struct BooleanObjectStruct booleanObjects[2];
struct memorySpaceStruct {
char *space;
char *allocPtr;
char *copyPtr;
char *scanPtr;
unsigned long size;
};
/* These two variables represent information about the semispaces. spaces
holds the information for each semispace (basically the pointer to the
base of the space, and the pointers into it for allocation, copying, and
scanning. curSpace holds the address of one of the two semispace data
structures, and is used by the garbage collector */
static struct memorySpaceStruct spaces[2];
static struct memorySpaceStruct *curSpace;
static Object curObject;
static long curObjectSize, copyQuota;
static double copyRate;
static double copyRateAdjustment = 1.50; /* copy 50% more than last time */
static long oopTableIndex;
static Object moveObject();
static void initCharObject(), moveRootOOPs(),
initSpace(), markBuiltinOOPs(),
copyReferencedObjects(), finishOOPScan(), clearOldOOPs();
#ifdef remove_soon /* Tue May 15 01:23:08 1990 */
/**/static char marks[TOTAL_OOP_TABLE_SLOTS];
/**/static void zeroMarks();
/**/static int traceOOPStuff = 0;
#endif /* remove_soon Tue May 15 01:23:08 1990 */
/*
* void initOOPTable()
*
* Description
*
* Initialize the OOP table. Initially, all the OOPs are on the OOP free
* list so that's just how we initialize them. We do as much
* initialization as we can, but we're called before classses are
* defined, so things that have definite classes must wait until
* the classes are defined.
*
*/
void initOOPTable()
{
int i;
#ifdef remove_soon /* Mon May 14 23:41:50 1990 */
/**/zeroMarks();
#endif /* remove_soon Mon May 14 23:41:50 1990 */
numFreeOOPs = OOP_TABLE_SIZE;
for (i = 0; i < OOP_TABLE_SIZE; i++) {
oopTable[i].object = (Object)&oopTable[i+1];
oopTable[i].isFree = 1; /* setting this free bit */
}
oopTable[i-1].object = nil;
freeOOPs = oopTable;
nilOOP = &oopTable[nilOOPIndex];
trueOOP = &oopTable[trueOOPIndex];
falseOOP = &oopTable[falseOOPIndex];
nilOOP->isFree = trueOOP->isFree = falseOOP->isFree = 0;
nilOOP->emptyBytes = trueOOP->emptyBytes = falseOOP->emptyBytes = 0;
nilOOP->object = (Object)&nilObject;
nilObject.objSize = ROUNDED_WORDS(sizeof(struct NilObjectStruct));
trueOOP->object = (Object)&booleanObjects[0];
falseOOP->object = (Object)&booleanObjects[1];
booleanObjects[0].objSize = ROUNDED_WORDS(sizeof(struct BooleanObjectStruct));
booleanObjects[1].objSize = ROUNDED_WORDS(sizeof(struct BooleanObjectStruct));
booleanObjects[0].booleanValue= trueOOP;
booleanObjects[1].booleanValue= falseOOP;
}
/*
* void initNil()
*
* Description
*
* Initialize the "nil" object.
*
*/
void initNil()
{
nilObject.objClass = undefinedObjectClass;
}
/*
* void initBooleans()
*
* Description
*
* Initialize the two boolean objects, after their respective classes have
* been created.
*
*/
void initBooleans()
{
booleanObjects[0].objClass = trueClass;
booleanObjects[1].objClass = falseClass;
}
/*
* void initCharTable()
*
* Description
*
* Initialize the instances of the class Character, after that class has
* been created.
*
*/
void initCharTable()
{
int i;
for (i = 0; i < NUM_CHAR_OBJECTS; i++) {
initCharObject(i);
oopTable[i + CHAR_OBJECT_BASE].object = (Object)&charObjectTable[i];
oopTable[i + CHAR_OBJECT_BASE].isFree = 0;
oopTable[i + CHAR_OBJECT_BASE].emptyBytes = 0;
}
}
/*
* static void initCharObject(i)
*
* Description
*
* Initialize a single character object.
*
* Inputs
*
* i : The index of the character object, in the range 0..255.
*
*/
static void initCharObject(i)
int i;
{
charObjectTable[i].objSize = ROUNDED_WORDS(sizeof(CharObject));
charObjectTable[i].objClass = charClass;
charObjectTable[i].charVal = i;
}
/*
* void fixupMetaclassObjects()
*
* Description
*
* Called after the fundamental class hierarchy has been defined, this
* function goes through and fixes up all the objects in the oop table
* that don't have a objClass (objClass == nilOOP). It's a
* chicken-and-egg problem: the metaclassClass doesn't yet exist when the
* hierarchy is put together, so after it's created, we have to go back
* and fix all the metaclasses that we created.
*
*/
void fixupMetaclassObjects()
{
int i;
for (i = 0; i < OOP_TABLE_SIZE; i++) {
if (!oopTable[i].isFree && isNil(oopTable[i].object->objClass)) {
oopTable[i].object->objClass = metaclassClass;
}
}
}
/*
* OOP findAnInstance(classOOP)
*
* Description
*
* Finds and returns an instance of the class CLASSOOP. Returns "nil" if
* there are no instances present.
*
* Inputs
*
* classOOP:
* OOP for a class for which to find an instance
*
* Outputs
*
* The first instance of the given class in the OOP table.
*/
OOP findAnInstance(classOOP)
OOP classOOP;
{
int i;
for (i = 0; i < OOP_TABLE_SIZE; i++) {
if (!oopTable[i].isFree && oopTable[i].object->objClass == classOOP) {
return (&oopTable[i]);
}
}
return (nilOOP);
}
/*
* long oopIndex(oop)
*
* Description
*
* Returns the index within the OOP table of the given OOP.
*
* Inputs
*
* oop : OOP to return index of
*
* Outputs
*
* Returned index in the OOP table, in range 0..TOTAL_OOP_TABLE_SLOTS.
*/
long oopIndex(oop)
OOP oop;
{
return (oop - oopTable);
}
/*
* Boolean oopIndexValid(index)
*
* Description
*
* Checks to see if index represents a valid OOP.
*
* Inputs
*
* index : a long index into the OOP table, apparently 1 based due to
* being called from Smalltalk via a primitive.
*
* Outputs
*
* True if the index represents a valid OOP table element, false
* otherwise.
*/
Boolean oopIndexValid(index)
long index;
{
return (index >= 1 && index <= TOTAL_OOP_TABLE_SLOTS);
}
#ifndef INLINE_MACROS
OOP oopAt(index)
long index;
{
return (oopAtMac(index));
}
#endif /* INLINE_MACROS */
void swapObjects(oop1, oop2)
OOP oop1, oop2;
{
struct OOPStruct tempOOP;
tempOOP = *oop2;
*oop2 = *oop1;
*oop1 = tempOOP;
}
OOP charOOPAt(c)
Byte c;
{
return (&oopTable[c + CHAR_OBJECT_BASE]);
}
Byte charOOPValue(charOOP)
OOP charOOP;
{
return (charOOP - &oopTable[CHAR_OBJECT_BASE]);
}
void printObject(oop)
OOP oop;
{
if (isInt(oop)) {
printf("%d", toInt(oop));
} else if (isNil(oop)) {
printf("nil");
} else if (oop == trueOOP) {
printf("true");
} else if (oop == falseOOP) {
printf("false");
} else if (oopClass(oop) == charClass) {
printf("$%c", charOOPValue(oop));
} else if (oopClass(oop) == floatClass) {
printf("%#g", floatOOPValue(oop));
} else if (oopClass(oop) == symbolClass) {
printf("#"); printSymbol(oop);
} else if (oopClass(oop) == stringClass) {
/* ### have to quote embedded quote chars */
printf("'");
printString(oop);
printf("'");
} else {
printOOPConstructor(oop);
}
}
Boolean oopValid(oop)
OOP oop;
{
return (!oop->isFree && (oop->oddMark || oop->evenMark));
}
#ifndef INLINE_MACROS
Boolean oopAvailable(index)
long index;
{
return (oopAvailableMac(index);
}
#endif /* INLINE_MACROS */
/*
* OOP allocOOP(obj)
*
* Description
*
* Given an object OBJ, this routine allocates an OOP table slot for it
* and returns it. It marks the OOP so that it indicates the object is in
* new space, and that the oop has been referenced on this pass (to keep
* the OOP table reaper from reclaiming this OOP).
*
* Inputs
*
* obj : Object that the new OOP should point to.
*
* Outputs
*
* An OOP, which is the address of an element in the OOP table.
*/
OOP allocOOP(obj)
Object obj;
{
OOP oop;
int i;
for (i = 0; i < oopReclaimFactor; i++) {
if (oopTableIndex >= OOP_TABLE_SIZE) {
break;
}
oop = &oopTable[oopTableIndex++];
if (!oop->isFree) {
if (!oop->evenMark && !oop->oddMark) {
#ifdef remove_soon /* Mon May 14 23:42:47 1990 */
/**/if (traceOOPStuff) {
/**/printf("freeing %d\n", oopTableIndex - 1);
/**/}
#endif /* remove_soon Mon May 14 23:42:47 1990 */
#ifdef remove_soon /* Tue May 15 02:23:58 1990 */
/**/ if (marks[oopTableIndex - 1]) {
/**/ printf("freeling link %d\n", oopTableIndex -1 );
/**/ }
#endif /* remove_soon Tue May 15 02:23:58 1990 */
/* we've found a dead one...add it to the free list */
numFreeOOPs++;
oop->object = (Object)freeOOPs;
freeOOPs = oop;
freeOOPs->isFree = true;
} else if (toSpace) {
#ifdef remove_soon /* Mon May 14 23:42:54 1990 */
/**/if (oop == &oopTable[100]) { /* ### */
/**/printf("clearing 101 even\n");
/**/}
#endif /* remove_soon Mon May 14 23:42:54 1990 */
oop->evenMark = 0;
#ifdef remove_soon /* Tue May 15 02:23:47 1990 */
/**/ if (marks[oopTableIndex - 1]) {
/**/ printf("even marking %d\n", oopTableIndex -1 );
/**/ }
#endif /* remove_soon Tue May 15 02:23:47 1990 */
} else {
#ifdef remove_soon /* Mon May 14 23:42:59 1990 */
/**/if (oop == &oopTable[100]) { /* ### */
/**/printf("clearing 101 odd\n");
/**/}
#endif /* remove_soon Mon May 14 23:42:59 1990 */
oop->oddMark = 0;
#ifdef remove_soon /* Tue May 15 02:23:31 1990 */
/**/ if (marks[oopTableIndex - 1]) {
/**/ printf("odd marking %d\n", oopTableIndex -1);
/**/ }
#endif /* remove_soon Tue May 15 02:23:31 1990 */
}
#ifdef remove_soon /* Mon May 14 23:42:19 1990 */
/**/if (traceOOPStuff)
/**/{ int i;
/**/
/**/ i = oop - oopTable;
/**/if (marks[i] != 0) {
/**/ printf("AllocOOP: oop %d value %d even %d odd %d\n", i, marks[i],
/**/ oop->evenMark, oop->oddMark);
/**/}
/**/marks[i] = 2; /* mark as freed */
/**/}
#endif /* remove_soon Mon May 14 23:42:19 1990 */
}
}
oop = freeOOPs;
numFreeOOPs--;
if (oop == nil) {
errorf("Ran out of OOP Table slots!!!");
exit(1);
/* ### this needs to be fixed */
}
if (!oop->isFree) {
errorf("Allocating allocated OOP!!!");
exit(0);
}
freeOOPs = (OOP)oop->object;
if (objSpace(obj) != toSpace) {
obj = moveObject(obj);
}
#ifdef remove_soon /* Mon May 14 23:43:09 1990 */
/**/if (oop == &oopTable[100] || oop == &oopTable[123]) { /* ### */
/**/printf("allocating %d\n", oop - oopTable);
/**/debug();
/**/}
#endif /* remove_soon Mon May 14 23:43:09 1990 */
oop->object = obj;
oop->isFree = false;
oop->emptyBytes = 0;
oop->inSpace = toSpace;
oop->oddMark = toSpace;
oop->evenMark = !toSpace;
#ifdef remove_soon /* Mon May 14 23:42:32 1990 */
/**/{ extern int okToPrint;
/**/if (traceOOPStuff && okToPrint) {
/**/ static Boolean dumped = false;
/**/ if (!dumped) {
/**/ OOP o;
/**/ dumped = true;
/**/ for (o = oopTable; o < oop; o++) {
/**/ printf("Object %d is ", o - oopTable); printObject(o);
/**/ printf("\n");
/**/ }
/**/ }
/**/if (oop - oopTable > 23000) {
/**/ printf("Allocated %d = ", oop - oopTable); printObject(oop);
/**/ printf("\n");
/**/}
/**/}
/**/}
#endif /* remove_soon Mon May 14 23:42:32 1990 */
#ifdef remove_soon /* Tue May 15 02:23:14 1990 */
/**/
/**/ if (oopClass(oop) == symLinkClass) {
/**/ marks[oop - oopTable] = 1;
/**/ printf("Allocating link %d: ", oop-oopTable);
/**/ printSymLink(oop);
/**/ printf("\n");
/**/ }
#endif /* remove_soon Tue May 15 02:23:14 1990 */
return (oop);
}
/*
* void setOOPObject(oop, object)
*
* Description
*
* Sets the object of OOP to be OBJECT. Makes sure that the object is in
* new space before it assigns it to OOP.
*
* Inputs
*
* oop : an OOP table entry to be assigned into
* object: an object that the OOP should point to.
*
*/
void setOOPObject(oop, object)
OOP oop;
Object object;
{
if (objSpace(object) != toSpace) {
object = moveObject(object);
}
oop->object = object;
oop->inSpace = toSpace;
}
/*
* void initMem()
*
* Description
*
* Initialize the memory allocator. Both semispaces are allocated, and
* the various garbage collection flags are set to their initial values.
*
*/
void initMem()
{
int i;
for (i = 0; i < 2; i++) {
spaces[i].space = (char *)malloc(MEM_SPACE_SIZE);
if (spaces[i].space == NULL) {
printf("Malloc failure; you're out of paging/swapping space\n");
exit(1);
}
initSpace(&spaces[i]);
}
curSpace = &spaces[0];
toSpace = 0;
fromSpace = !toSpace;
gcFlipped = false;
gcState = false;
gcMessage = true;
copyRate = 0.0; /* don't copy anything until first flip */
copyQuota = 0;
oopTableIndex = 0;
markBuiltinOOPs();
clearGCFlipFlags();
}
Object curSpaceAddr()
{
return ((Object)curSpace->space);
}
void setSpaceInfo(size)
long size;
{
curSpace->copyPtr = curSpace->scanPtr = curSpace->space + size;
curSpace->size -= size;
}
#ifndef INLINE_MACROS
void clearGCFlipFlags()
{
clearGCFlipFlagsMac();
}
#endif /* INLINE_MACROS */
/*
* Object allocObj(size)
*
* Description
*
* Allocate and return space for an object of SIZE bytes. This basically
* means moving the allocation pointer for the current space down by SIZE
* bytes, and, if there isn't enough space left, flipping the garbage
* collector to switch semispaces. The space is merely allocated; it is
* not initialized.
*
* Inputs
*
* size : size in bytes of the object to allocate. This will be rounded
* by this routine up to a suitable boundary, typically to a 4
* byte boundary.
*
* Outputs
*
* Address of the newly allocated object.
*/
Object allocObj(size)
long size;
{
size = alignSize(size);
copyReferencedObjects((long)(size * copyRate));
curSpace->allocPtr -= size;
if (curSpace->allocPtr <= curSpace->copyPtr) {
gcFlip();
curSpace->allocPtr -= size;
}
return ((Object)curSpace->allocPtr);
}
/*
* static void copyReferencedObjects(numBytes)
*
* Description
*
* This is the heart of the garbage collector. It is told that NUMBYTES
* have been allocated, and it sweeps through a proportional number of
* objects that have been copied into new space, looking for references to
* objects that are still in old space. It has to special case
* CompiledMethod objects due to their unusual structure. It uses global
* variables to preserve its state so that it can sweep through only as
* many objects as it wants, and resume sweeping on the next allocation
* where it left off.
*
* Inputs
*
* numBytes:
* The number of bytes that have been allocated. Used as a
* parameter that controls how much space to sweep on this
* iteration.
*
*/
static void copyReferencedObjects(numBytes)
long numBytes;
{
Object result, object;
OOP curClass;
InstanceSpec instanceSpec;
int stepSize, i;
Method method;
copyQuota += numBytes;
while (curSpace->scanPtr < curSpace->copyPtr
&& copyQuota > 0) { /* there's more to scan */
if (curObject == nil) {
/* if there is no current object, start off with the object's class */
object = (Object)curSpace->scanPtr;
curClass = object->objClass;
maybeMoveOOP(curClass);
if (curClass == compiledMethodClass) {
/* Compiled methods have to be dealt with specially since they
* have a structure that's unlike a regular Smalltalk object:
* it has two fixed instance variables (description and
* methodHeader), a variable number of literals, and then
* a bunch of bytecodes, which must be skipped over */
stepSize = object->objSize * sizeof(OOP);
method = (Method)object;
maybeMoveOOP(method->descriptor);
if (method->header.headerFlag == 0 || method->header.headerFlag == 3) {
for (i = 0; i < method->header.numLiterals; i++) {
maybeMoveOOP(method->literals[i]);
}
}
curSpace->scanPtr += stepSize;
copyQuota -= stepSize;
} else if (!classIsPointers(curClass)) {
/* nothing to scan, just skip over it */
stepSize = object->objSize * sizeof(OOP);
curSpace->scanPtr += stepSize;
copyQuota -= stepSize;
} else {
/* we've got an object with sub structure, so we set the object
* size pointer to 2 words less than the object size (ignore
* the header; we've already copied the class) and set the
* scan pointer to the first word of the object. We then continue
* to go through the normal scanning procedure (fall out the
* bottom of the if and go back to the top of the loop again)
*/
curObject = object;
curObjectSize = numOOPs(curObject) * sizeof(OOP);
curSpace->scanPtr = (char *)curObject->data;
}
} else {
/* we're part way through scanning an object, continue to scan... */
if (curObjectSize <= 0) {
curObject = nil;
} else {
maybeMoveOOP(*(OOP *)curSpace->scanPtr);
stepSize = sizeof(OOP);
curSpace->scanPtr += stepSize;
curObjectSize -= stepSize;
copyQuota -= stepSize;
}
}
}
if (copyQuota < 0) {
copyQuota = 0;
}
}
/*
* Boolean gcOff()
*
* Description
*
* Turns off the garbage collector. Returns the previous on/off state.
*
* Outputs
*
* Previous state of the garbage collector (on or off).
*/
Boolean gcOff()
{
Boolean oldGCState;
oldGCState = gcState;
gcState = false;
return (oldGCState);
}
/*
* void gcOn()
*
* Description
*
* Turns on the garbage collector.
*
*/
void gcOn()
{
gcState = true;
}
/*
* void setGCState(state)
*
* Description
*
* Set the garbage collector flag to the specified state (either on or
* off).
*
* Inputs
*
* state : Boolean, true => gc on.
*
*/
void setGCState(state)
Boolean state;
{
gcState = state;
}
/*
* void fullGC()
*
* Description
*
* Perform a complete garbage collection of the system. This basically
* copies all the remaining referenced objects to new space, flips old and
* new space, copies the root set to the now new space, and then copies
* all the referenced objects to the now new space.
*
*/
void fullGC()
{
if (!gcState) {
/* can't do a full GC with gc turned off! */
return;
}
copyReferencedObjects(0x7FFFFFFF); /* copy what we can */
gcFlip();
copyReferencedObjects(0x7FFFFFFF);
clearOldOOPs();
}
/*
* static void finishOOPScan()
*
* Description
*
* Scans through the OOP table, finishing the incremental OOP table
* scanning that goes on as OOPs are allocated.
*
*/
static void finishOOPScan()
{
OOP oop;
int i;
for (i = oopTableIndex; i < OOP_TABLE_SIZE; i++) {
oop = &oopTable[i];
if (!oop->isFree) {
if (!oop->evenMark && !oop->oddMark) {
/* we've found a dead one...add it to the free list */
numFreeOOPs++;
oop->object = (Object)freeOOPs;
freeOOPs = oop;
freeOOPs->isFree = true;
/* printf("Freed %d\n", i); */
} else if (toSpace) {
oop->evenMark = 0;
} else {
oop->oddMark = 0;
}
}
}
}
/*
* static void clearOldOOPs()
*
* Description
*
* Scans through the OOP table, removing OOPS that have died. Only
* called at the end of a full GC to remove any stragglers.
*
*/
static void clearOldOOPs()
{
OOP oop;
int i;
for (i = oopTableIndex; i < OOP_TABLE_SIZE; i++) {
oop = &oopTable[i];
if (!oop->isFree) {
if ((toSpace && !oop->oddMark) || (!toSpace && !oop->evenMark)) {
/* we've found a dead one...add it to the free list */
numFreeOOPs++;
oop->object = (Object)freeOOPs;
freeOOPs = oop;
freeOOPs->isFree = true;
/* printf("Freed %d\n", i); */
} else if (toSpace) {
oop->evenMark = 0;
} else {
oop->oddMark = 0;
}
}
}
}
/*
* void gcFlip()
*
* Description
*
* Switches the garbage collector's notion of which space is "new" space
* and which is "old" space. Readjusts the garbage collection parameters
* based on things like the allocation to copying ratio. Copies the root
* set to new space.
*
*/
void gcFlip()
{
long oldCopySize, oldNewSize;
if (!gcState) {
errorf("Attempted to do a gcFlip with garbage collector off!");
exit(1);
}
if (gcFlipCounter >= 1) {
errorf("Attempted to do a gcFlip too soon after a gcFlip!");
exit(1);
}
#ifdef OOP_DEBUGGING
printf("%d free oops = %.2f%%, scanner was at %d/%d\n", numFreeOOPs,
100.0 * numFreeOOPs / OOP_TABLE_SIZE, oopTableIndex, OOP_TABLE_SIZE);
#endif
if (gcMessage && !regressionTesting) {
/* print the first part of this message before we finish scanning
* oop table for live ones, so that the delay caused by this scanning
* is apparent.
*/
printf("\"GC flipping "); fflush(stdout);
}
finishOOPScan(); /* make sure we're done */
oldCopySize = curSpace->copyPtr - curSpace->space;
oldNewSize = curSpace->space + MEM_SPACE_SIZE - curSpace->allocPtr;
if (oldCopySize == 0) { /* ### Experimental */
oldCopySize = oldNewSize;
}
copyRate = ((double)oldCopySize) / oldNewSize;
copyRate *= copyRateAdjustment;
toSpace = !toSpace;
fromSpace = !fromSpace;
curSpace = &spaces[toSpace];
curObject = nil;
copyQuota = 0;
oopTableIndex = 0;
initSpace(curSpace);
#ifdef remove_soon /* Mon May 14 23:41:55 1990 */
/**/zeroMarks();
#endif /* remove_soon Mon May 14 23:41:55 1990 */
/* note the use of quotation marks around the printed message. The
idea here was to make them appear as Smalltalk comments, so that
generated output could be fed to another Smalltalk without harm. */
if (gcMessage && !regressionTesting) {
printf("to space %d...", toSpace); fflush(stdout);
printf("copied space = %.1f%%...", oldCopySize * 100.0 / MEM_SPACE_SIZE);
}
moveRootOOPs();
if (gcMessage && !regressionTesting) {
printf("done\"\n");
}
gcFlipped = true;
}
/*
* static void moveRootOOPs()
*
* Description
*
* Copies the root objects from old space to new space. All of the root
* objects are those that are mentioned in the set of objects that are
* known specially by the interpreter, those that are being used by the
* interpreter, and some information about compilation state. Also, the
* built-in oops (Characters, nil, true, false) are marked as being in new
* space so that they won't ever be moved.
*
*/
static void moveRootOOPs()
{
OOP **oopPtr, oop;
int i;
markBuiltinOOPs();
/* copy objects that have global pointers */
for (oopPtr = globalOOPs; *oopPtr; oopPtr++) {
oop = **oopPtr;
maybeMoveOOP(oop); /* use the maybe form here so that we don't
* accidentally move builtins, which have
* already been marked as being in the new
* space
*/
}
moveProcessorRegisters();
copyCompileContext();
}
/*
* static void markBuiltinOOPs()
*
* Description
*
* Marks all of the builtin OOPS (nil, true, false, and the Characters) as
* being in the current new space.
*
*/
static void markBuiltinOOPs()
{
int i;
for (i = OOP_TABLE_SIZE; i < TOTAL_OOP_TABLE_SLOTS; i++) {
oopTable[i].inSpace = toSpace;
oopTable[i].evenMark = oopTable[i].oddMark = 1;
}
}
/*
* static void initSpace(space)
*
* Description
*
* Initializes the allocation and copying pointers for semispace SPACE.
*
* Inputs
*
* space : Semispace index number.
*
*/
static void initSpace(space)
struct memorySpaceStruct *space;
{
space->copyPtr = space->scanPtr = space->space;
space->size = MEM_SPACE_SIZE;
space->allocPtr = space->space + space->size;
}
#ifndef INLINE_MACROS
/*
* void maybeMoveOOP(oop)
*
* Description
*
* Move OOP to new space if it's not already there.
*
* Inputs
*
* oop : OOP to be examined, and, if it's in old space, moved to new
* space.
*
*/
void maybeMoveOOP(oop)
OOP oop;
{
maybeMoveOOPMac(oop);
}
#endif /* INLINE_MACROS */
/*
* void moveOOP(oop)
*
* Description
*
* Moves an OOP from old space to new space unconditionally. Basically
* marks the OOP as being in the current new space, copies the object that
* the oop points to to new space, and sets the even/odd flags to keep the
* OOP table garbage collector from reaping this OOP.
*
* Inputs
*
* oop : OOP to be moved. Should always be in OLD space.
*
*/
void moveOOP(oop)
OOP oop;
{
Object object;
object = oopToObj(oop);
oop->inSpace = toSpace;
oop->object = moveObject(object);
#ifdef remove_soon /* Mon May 14 23:42:39 1990 */
/**/if (traceOOPStuff)
/**/{ int i;
/**/
/**/ i = oop - oopTable;
/**/if (marks[i] != 0) {
/**/ printf("moveOOP: oop %d has a mark value of %d\n", i, marks[i]);
/**/}
/**/marks[i] = 1;
/**/}
#endif /* remove_soon Mon May 14 23:42:39 1990 */
if (toSpace) {
oop->oddMark = 1;
} else {
oop->evenMark = 1;
}
}
/*
* static Object moveObject(object)
*
* Description
*
* Copies OBJECT from old space to new space. Adjusts the garbage
* collectors pointers to indicate that the object has been added to new
* space so that the scanner will see it.
*
* Inputs
*
* object: Object to be moved to new space.
*
*/
static Object moveObject(object)
Object object;
{
int size;
size = object->objSize * sizeof(OOP);
memcpy(curSpace->copyPtr, object, size);
object = (Object)curSpace->copyPtr;
curSpace->copyPtr += size;
curSpace->size -= size;
if (curSpace->copyPtr >= curSpace->allocPtr) {
errorf("Garbage collector failed...ran out of room while copying!!!");
exit(0);
}
return (object);
}
/*
* void printFreeList()
*
* Description
*
* Debugging support routine. Prints the free list. Meant only to be
* called from a debugger.
*
*/
void printFreeList()
{
OOP oop;
for (oop = freeOOPs; oop != nil; oop = (OOP)oop->object) {
printf("oop %x\n", oop);
printf("oop isfree %d\n", oop->isFree);
}
}
/*
* debug()
*
* Description
*
* Used for debugging. You set a breakpoint in the debug routine in the
* debugger, and have code call it when you want it to stop. Performs no
* action normally.
*
*/
debug()
{
}
#ifdef remove_soon /* Mon May 14 23:42:01 1990 */
/**/static void zeroMarks()
/**/{
/**/ char *p;
/**/
/**/ for (p = marks; p < &marks[TOTAL_OOP_TABLE_SLOTS]; ) {
/**/ *p++ = 0;
/**/ }
/**/}
#endif /* remove_soon Mon May 14 23:42:01 1990 */